﻿using System;
using System.Globalization;

namespace GeoAPI.DataStructures
{
	[Serializable]
	public struct Interval : IEquatable<Interval>
	{
		private Interval(double value)
		{
			this.Min = value;
			this.Max = value;
		}

		private Interval(double min, double max)
		{
			this.Min = min;
			this.Max = max;
		}

		public Interval ExpandedByValue(double p)
		{
			if (p.Equals(double.NaN))
			{
				return this;
			}
			if (this.Min.Equals(double.NaN))
			{
				return new Interval(p, p);
			}
			double min = (p < this.Min) ? p : this.Min;
			double max = (p > this.Max) ? p : this.Max;
			return new Interval(min, max);
		}

		private bool IsEmpty
		{
			get
			{
				return this.Min.Equals(double.NaN);
			}
		}

		public override int GetHashCode()
		{
			return 52876 ^ this.Min.GetHashCode();
		}

		public override bool Equals(object obj)
		{
			return obj is Interval && this.Equals((Interval)obj);
		}

		public bool Equals(Interval other)
		{
			return !(this.IsEmpty ^ other.IsEmpty) && ((this.IsEmpty && other.IsEmpty) || (this.Min == other.Min && this.Max == other.Max));
		}

		public override string ToString()
		{
			if (this.IsEmpty)
			{
				return "Interval ℝ[Uninitialized]";
			}
			return string.Format(NumberFormatInfo.InvariantInfo, "Interval ℝ[{0}, {1}] (Width={2}) ", new object[]
			{
				this.Min,
				this.Max,
				this.Width
			});
		}

		public double Width
		{
			get
			{
				return this.Max - this.Min;
			}
		}

		public double Centre
		{
			get
			{
				return this.Min + this.Width * 0.5;
			}
		}

		public Interval ExpandedByInterval(Interval interval)
		{
			if (this.IsEmpty && interval.IsEmpty)
			{
				return Interval.Create();
			}
			if (!this.IsEmpty && interval.IsEmpty)
			{
				return this;
			}
			if (this.IsEmpty && !interval.IsEmpty)
			{
				return interval;
			}
			double min = (this.Min < interval.Min) ? this.Min : interval.Min;
			double max = (this.Max > interval.Max) ? this.Max : interval.Max;
			return new Interval(min, max);
		}

		public bool Overlaps(Interval interval)
		{
			return this.Overlaps(interval.Min, interval.Max);
		}

		public bool Overlaps(double min, double max)
		{
			return this.Min <= max && this.Max >= min;
		}

		public bool Contains(Interval interval)
		{
			return this.Contains(interval.Min, interval.Max);
		}

		public bool Contains(double min, double max)
		{
			return min >= this.Min && max <= this.Max;
		}

		public bool Contains(double p)
		{
			return p >= this.Min && p <= this.Max;
		}

		public bool Intersects(Interval other)
		{
			return this.Intersects(other.Min, other.Max);
		}

		public bool Intersects(double min, double max)
		{
			return min <= this.Max && max >= this.Min;
		}

		public static Interval Create()
		{
			return new Interval(double.NaN);
		}

		public static Interval Create(double value)
		{
			return new Interval(value);
		}

		public static Interval Create(double val1, double val2)
		{
			if (val1 >= val2)
			{
				return new Interval(val2, val1);
			}
			return new Interval(val1, val2);
		}

		public static Interval Create(Interval interval)
		{
			return new Interval(interval.Min, interval.Max);
		}

		public static bool operator ==(Interval lhs, Interval rhs)
		{
			return lhs.Equals(rhs);
		}

		public static bool operator !=(Interval lhs, Interval rhs)
		{
			return !lhs.Equals(rhs);
		}

		public readonly double Min;

		public double Max;
	}
}
